﻿using System;
using System.ComponentModel.Composition;
using Reactor.Configuration;
using Reactor.Customization;
using Reactor.Environment;
using Reactor.Exceptions;
using Reactor.Messaging;
using Reactor.Messaging.ServiceBus;
using Reactor.Messaging.ServiceBus.Internals;
using Reactor.ServiceBus.Transports.ActiveMQ;

namespace Reactor.ServiceBus.Configuration.Default
{
    [Export(typeof(ICustomizeReactorInitialization))]
    public class DefaultServiceBusCustomizer : ICustomizeReactorInitialization
    {
        private readonly string _inputQueueName;

        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultServiceBusCustomizer"/> class. This constructor 
        /// is used by MEF and allows it to construct and provide this instance an input queue name.
        /// </summary>
        public DefaultServiceBusCustomizer() : this(InitializationCustomizationManager.PrimaryEndpointName) {}

        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultServiceBusCustomizer"/> class.
        /// </summary>
        /// <param name="inputQueueName">Name of the input queue.</param>
        public DefaultServiceBusCustomizer(string inputQueueName)
        {
            if (string.IsNullOrEmpty(inputQueueName)) throw new ArgumentNullException("inputQueueName");

            _inputQueueName = inputQueueName;
        }

        #region Implementation of ICustomizeReactorInitialization

        public uint ExecutionOrder
        {
            get { return 2; }
        }

        /// <summary>
        /// Initializes Reactor. Implementers can provide complete custom initialization of the Reactor Service, bypassing 
        /// all default initialization. Upon completion, validation of the Reactor Context is performed. If critical services are 
        /// not present on the Context, a fatal exception will cause the Reactor Service to terminate.
        /// </summary>
        public void InitializeReactor()
        {
            if(Context.ServiceLocator == null)
                throw new FatalException("No IServiceLocator instance found at Context.ServiceLocator. Configure a service locator instance prior to creating a service bus instance.");

            var serviceLocator = Context.ServiceLocator;    // Local reference so we dont hit context each time
            var platformConnectionData = serviceLocator.GetInstance<PlatformConnectionData>();

            // Can't continue if we have no broker connection details
            if(platformConnectionData.BrokerDetails == null || string.IsNullOrEmpty(platformConnectionData.BrokerDetails.Uri))
                throw new FatalException("Cannot configure service bus if no broker details are supplied.");

            var configuration = new BusConfiguration
            {
                DeadLetterChannel = new PointToPointChannel("Reactor.DLQ") { IsTransactional = false },
                ConnectionData = platformConnectionData,
                CurrentEndpointInputChannel = new PointToPointChannel(_inputQueueName)
            };

            serviceLocator.RegisterType<ISubscriptionDataProvider, SqlSubscriptionDataProvider>();
            serviceLocator.RegisterType<IDestinationRegistry, DefaultDestinationRegistry>();
            serviceLocator.RegisterType<IHandlerRegistry, DefaultHandlerRegistry>();
            serviceLocator.RegisterType<Subscriber, Subscriber>();

            // Obtain destination registry and initialize it
            configuration.DestinationRegistry = serviceLocator.GetInstance<IDestinationRegistry>();
            configuration.DestinationRegistry.Initialize();

            // Obtain message handler registry
            configuration.MessageHandlerRegistry = serviceLocator.GetInstance<IHandlerRegistry>();

            // Register bus configuration with the service locator
            serviceLocator.RegisterInstance<BusConfiguration>(configuration);

            // JMS instances to support Fuse broker usage
            var jmsManager = new ActiveMqGateway(configuration.ConnectionData.BrokerDetails);
            Context.ServiceLocator.RegisterInstance<INmsGateway>(jmsManager);

            // Obtain service bus instance
            Context.ServiceBus = new DefaultBus(jmsManager, configuration);
            serviceLocator.RegisterInstance<IServiceBus>(Context.ServiceBus);
        }

        #endregion
    }
}
